#include "jni_util.h"


JNIHandle::JNIHandle() = default;

JNIHandle::~JNIHandle() = default;

JNIHandle *JNIHandle::GetInstance() {
    static JNIHandle jniHandle;
    return &jniHandle;
}

void JNIHandle::Init(JNIEnv *env) {

    env->GetJavaVM(&jvm);

    /**Boolean*/
    jclass clsBoolean = env->FindClass("java/lang/Boolean");
    cls_boolean = (jclass) env->NewGlobalRef(clsBoolean);
    mid_boolean_init = env->GetMethodID(clsBoolean, "<init>", "(Z)V");
    env->DeleteLocalRef(clsBoolean);

    /**Long*/
    jclass clsLong = env->FindClass("java/lang/Long");
    cls_long = (jclass) env->NewGlobalRef(clsLong);
    fid_long_value = env->GetFieldID(clsLong, "value", "J");
    env->DeleteLocalRef(clsLong);


    /**List*/
    jclass tmpClsList = env->FindClass("java/util/List");
    cls_list = (jclass) env->NewGlobalRef(tmpClsList);
    mid_list_size = env->GetMethodID(tmpClsList, "size", "()I");
    mid_list_add = env->GetMethodID(tmpClsList, "add", "(Ljava/lang/Object;)Z");
    mid_list_get = env->GetMethodID(tmpClsList, "get", "(I)Ljava/lang/Object;");
    env->DeleteLocalRef(tmpClsList);

    /** ArrayList */
    jclass tmpClsArrayList = env->FindClass("java/util/ArrayList");
    cls_arraylist = (jclass)(env->NewGlobalRef(tmpClsArrayList));
    mid_arraylist_init = env->GetMethodID(tmpClsArrayList, "<init>", "()V");
    mid_arraylist_add = env->GetMethodID(tmpClsArrayList, "add", "(Ljava/lang/Object;)Z");
    env->DeleteLocalRef(tmpClsArrayList);

    /**ApiResult*/
    jclass tmpClsApiResult = env->FindClass("com/arcvideo/ocean/vag/jni/ApiResult");
    cls_apiResult = (jclass) env->NewGlobalRef(tmpClsApiResult);
    mid_apiResult_init = env->GetMethodID(tmpClsApiResult, "<init>", "(I)V");
    env->DeleteLocalRef(tmpClsApiResult);

    /**DauDeviceInfo*/
    jclass tmpClsDauDeviceInfo = env->FindClass("com/arcvideo/ocean/vag/jni/DauDeviceInfo");
    cls_dauDeviceInfo = (jclass) env->NewGlobalRef(tmpClsDauDeviceInfo);

    // The construct order is: String name, String serialnum, String version, String devType
    mid_dauDeviceInfo_init = env->GetMethodID(tmpClsDauDeviceInfo, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
    fid_dauDeviceInfo_name = env->GetFieldID(tmpClsDauDeviceInfo, "name", "Ljava/lang/String;");
    fid_dauDeviceInfo_serialnum = env->GetFieldID(tmpClsDauDeviceInfo, "serialnum", "Ljava/lang/String;");
    fid_dauDeviceInfo_version = env->GetFieldID(tmpClsDauDeviceInfo, "version", "Ljava/lang/String;");
    fid_dauDeviceInfo_devType = env->GetFieldID(tmpClsDauDeviceInfo, "devType", "Ljava/lang/String;");

    env->DeleteLocalRef(tmpClsDauDeviceInfo);


    /**DeviceInfo*/
    jclass tmpClsDeviceInfo = env->FindClass("com/arcvideo/ocean/vag/jni/DeviceInfo");
    cls_deviceInfo = (jclass) env->NewGlobalRef(tmpClsDeviceInfo);
//    mid_deviceInfo_init = env->GetMethodID(tmpClsDeviceInfo, "<init>", "(I)V");
    fid_deviceInfo_ip = env->GetFieldID(tmpClsDeviceInfo, "ip", "Ljava/lang/String;");
    fid_deviceInfo_dev_uuid = env->GetFieldID(tmpClsDeviceInfo, "devUUID", "Ljava/lang/String;");
    fid_deviceInfo_port = env->GetFieldID(tmpClsDeviceInfo, "port", "I");
    fid_deviceInfo_username = env->GetFieldID(tmpClsDeviceInfo, "username", "Ljava/lang/String;");
    fid_deviceInfo_password = env->GetFieldID(tmpClsDeviceInfo, "password", "Ljava/lang/String;");
    fid_deviceInfo_type = env->GetFieldID(tmpClsDeviceInfo, "type", "Lcom/arcvideo/ocean/vag/jni/DeviceInfo$Type;");
    fid_deviceInfoLinkMode = env->GetFieldID(tmpClsDeviceInfo, "linkMode",
                                             "Lcom/arcvideo/ocean/vag/jni/DeviceInfo$LinkMode;");
    env->DeleteLocalRef(tmpClsDeviceInfo);

    /**DevChannelInfo*/
    jclass tmpClsDevChannelInfo = env->FindClass("com/arcvideo/ocean/vag/jni/DevChannelInfo");
    cls_devChannelInfo = (jclass) env->NewGlobalRef(tmpClsDevChannelInfo);
    mid_devChannelInfo_init = env->GetMethodID(tmpClsDevChannelInfo, "<init>", "(ILjava/util/List;)V");
    fid_devChannelInfo_channelNum = env->GetFieldID(tmpClsDevChannelInfo, "channelNum", "I");
    fid_devChannelInfo_channelNameInfo = env->GetFieldID(tmpClsDevChannelInfo, "channelNameInfo",
                                            "Ljava/util/List;");
    env->DeleteLocalRef(tmpClsDevChannelInfo);


    /**ChannelNameInfo*/
    jclass clsChannelNameInfo = env->FindClass("com/arcvideo/ocean/vag/jni/DevChannelInfo$ChannelNameInfo");
    cls_channelNameInfo = (jclass) env->NewGlobalRef(clsChannelNameInfo);
    mid_channelNameInfo_init = env->GetMethodID(clsChannelNameInfo, "<init>", "(ILjava/lang/String;I)V");
    fid_channelNameInfo_index = env->GetFieldID(clsChannelNameInfo, "channelIndex", "I");
    fid_channelNameInfo_name = env->GetFieldID(clsChannelNameInfo, "channelName", "Ljava/lang/String;");
    fid_channelNameInfo_remoteIndex = env->GetFieldID(clsChannelNameInfo, "remoteIndex", "I");
    env->DeleteLocalRef(clsChannelNameInfo);

    /**DeviceInfoType*/
    jclass clsDeviceInfoType = env->FindClass("com/arcvideo/ocean/vag/jni/DeviceInfo$Type");
    fid_deviceInfoType_value = env->GetFieldID(clsDeviceInfoType, "value", "I");
    env->DeleteLocalRef(clsDeviceInfoType);

    /**DeviceInfoLinkMode*/
    jclass clsDeviceInfoLinkMode = env->FindClass("com/arcvideo/ocean/vag/jni/DeviceInfo$LinkMode");
    fid_deviceInfoLinkMode_value = env->GetFieldID(clsDeviceInfoLinkMode, "value", "I");
    env->DeleteLocalRef(clsDeviceInfoLinkMode);

    /**VagApiEx*/
    jclass clsVagApiEx = env->FindClass("com/arcvideo/ocean/vag/jni/VagApiException");
    cls_vagApi_ex = (jclass) env->NewGlobalRef(clsVagApiEx);
    env->DeleteLocalRef(clsVagApiEx);

    /**ShowStringInfo*/
    jclass clsShowStringInfo = env->FindClass("com/arcvideo/ocean/vag/jni/ShowStringInfo");
    cls_showStringInfo = (jclass) env->NewGlobalRef(clsShowStringInfo);
    mid_showStringInfo_init = env->GetMethodID(clsShowStringInfo, "<init>", "()V");
    fid_showStringInfo_osdInfoList = env->GetFieldID(clsShowStringInfo, "osdInfoList", "Ljava/util/List;");
    env->DeleteLocalRef(clsShowStringInfo);

    /**OsdInfo*/
    jclass clsOsdInfo = env->FindClass("com/arcvideo/ocean/vag/jni/OsdInfo");
    cls_osdInfo = (jclass) env->NewGlobalRef(clsOsdInfo);
    mid_osdInfo_init = env->GetMethodID(clsOsdInfo, "<init>", "()V");
    fid_osdInfo_iShowString = env->GetFieldID(clsOsdInfo, "iShowString", "Ljava/lang/Long;");
    fid_osdInfo_iStringSize = env->GetFieldID(clsOsdInfo, "iStringSize", "Ljava/lang/Long;");
    fid_osdInfo_iShowStringTopLeftX = env->GetFieldID(clsOsdInfo, "iShowStringTopLeftX", "Ljava/lang/Long;");
    fid_osdInfo_iShowStringTopLeftY = env->GetFieldID(clsOsdInfo, "iShowStringTopLeftY", "Ljava/lang/Long;");
    fid_osdInfo_text = env->GetFieldID(clsOsdInfo, "text", "Ljava/lang/String;");
    env->DeleteLocalRef(clsOsdInfo);

    /**SetUpAlarm*/
    jclass clsSetUpAlarm = env->FindClass("com/arcvideo/ocean/vag/jni/SetUpAlarm");
    cls_setUpAlarm = (jclass) env->NewGlobalRef(clsSetUpAlarm);
    fid_setUpAlarm_dwSize = env->GetFieldID(clsSetUpAlarm, "dwSize", "Ljava/lang/Long;");
    fid_setUpAlarm_byLevel = env->GetFieldID(clsSetUpAlarm, "byLevel", "I");
    fid_setUpAlarm_byAlarmInfoType = env->GetFieldID(clsSetUpAlarm, "byAlarmInfoType", "I");
    fid_setUpAlarm_byRetAlarmTypeV40 = env->GetFieldID(clsSetUpAlarm, "byRetAlarmTypeV40", "I");
    fid_setUpAlarm_byRetDevInfoVersion = env->GetFieldID(clsSetUpAlarm, "byRetDevInfoVersion", "I");
    fid_setUpAlarm_byRetVQDAlarmType = env->GetFieldID(clsSetUpAlarm, "byRetVQDAlarmType", "I");
    fid_setUpAlarm_byFaceAlarmDetection = env->GetFieldID(clsSetUpAlarm, "byFaceAlarmDetection", "I");
    fid_setUpAlarm_bySupport = env->GetFieldID(clsSetUpAlarm, "bySupport", "I");
    fid_setUpAlarm_byBrokenNetHttp = env->GetFieldID(clsSetUpAlarm, "byBrokenNetHttp", "I");
    fid_setUpAlarm_wTaskNo = env->GetFieldID(clsSetUpAlarm, "wTaskNo", "Ljava/lang/Long;");
    fid_setUpAlarm_byDeployType = env->GetFieldID(clsSetUpAlarm, "byDeployType", "I");
    fid_setUpAlarm_bySubScription = env->GetFieldID(clsSetUpAlarm, "bySubScription", "I");
    fid_setUpAlarm_byBrokenNetHttpV60 = env->GetFieldID(clsSetUpAlarm, "byBrokenNetHttpV60", "I");
    fid_setUpAlarm_byRes1 = env->GetFieldID(clsSetUpAlarm, "byRes1", "I");
    fid_setUpAlarm_byAlarmTypeURL = env->GetFieldID(clsSetUpAlarm, "byAlarmTypeURL", "I");
    fid_setUpAlarm_byCustomCtrl = env->GetFieldID(clsSetUpAlarm, "byCustomCtrl", "I");
    fid_setUpAlarm_byRes4 = env->GetFieldID(clsSetUpAlarm, "byRes4", "[B");
    env->DeleteLocalRef(clsSetUpAlarm);

    /** VagApi.alarmCallback */
    jclass tmpClsVagApi = env->FindClass("com/arcvideo/ocean/vag/jni/VagApi");
    cls_vagApi = (jclass)(env->NewGlobalRef(tmpClsVagApi));
    mid_vagApi_alarmCallback = env->GetStaticMethodID(cls_vagApi, "alarmCallback", "(Ljava/lang/String;)V");


}

std::string ConvertToStdString(JNIEnv *env, jstring jstr)
{
    std::string str;
    if (jstr != nullptr) {
        const char *pStr = env->GetStringUTFChars(jstr, JNI_FALSE);
        if (pStr != nullptr) {
            str.append(pStr);
            env->ReleaseStringUTFChars(jstr, pStr);
        }
    }
    return str;
}

void SetObjectStringField(JNIEnv *env, jobject obj, jfieldID fieldID, const std::string& str)
{
    const char *cStr = str.c_str();
    if (cStr != nullptr)
    {
        jstring value = env->NewStringUTF(cStr);
        env->SetObjectField(obj, fieldID, value);
        env->DeleteLocalRef(value);
    }
}

jobject NewArrayList(JNIEnv *env) {
    jclass clsArrayList = JNIHandle::GetInstance()->cls_arraylist;
    jmethodID mid_arraylist_init = JNIHandle::GetInstance()->mid_arraylist_init;
    return env->NewObject(clsArrayList, mid_arraylist_init);
}



